
package ploy.compiler;

import java.util.*;
import java.text.*;

import ploy.lang.*;

/**
 * A ParseClass represents a parse function for a Ploy expression.
 */
public abstract class ParseClass {
    
    public abstract AST parse( DynamicObject s, 
			       SyntacticEnvironment m )
	throws ParseException;

}

/**
 * (lambda (x y ...) e)
 */
class LambdaParser extends ParseClass {

    public List<Symbol> parseSymbolList( DynamicObject s )
	throws ParseException {
	List<Symbol> toReturn = new LinkedList<Symbol>();

	while ( s.isPair() ) {
	    DynamicObject first = ((Pair)s).head();
	    if ( !first.isSymbol() )
		throw new ParseException( "Non-symbol variable", 0 );
	    toReturn.add( (Symbol)first );
	    s = ((Pair)s).tail();
	}
	if ( !s.isNil() ) 
	    throw new ParseException( "Improper list in function declaration", 
				      0 );
	return toReturn;
    }
   
    public AST parse( DynamicObject s, SyntacticEnvironment m ) 
	throws ParseException {
	if ( s.isPair() && ((Pair)s).tail().isPair() &&
	     ((Pair)((Pair)s).tail()).tail().isPair() &&
	     ((Pair)((Pair)((Pair)s).tail()).tail()).tail().isNil() ) {
	    Pair p1 = (Pair)s;
	    DynamicObject car = p1.head();
	    DynamicObject cdr = p1.tail();
	    Pair p2 = (Pair)cdr;
	    DynamicObject cadr = p2.head();
	    DynamicObject cddr = p2.tail();
	    Pair p3 = (Pair)cddr;
	    DynamicObject caddr = p3.head();
	    return new Lambda( parseSymbolList( cadr ), 
			       AST.parse ( caddr, m ) );
	} else {
	    throw new ParseException( "Ill-formed function", 0 );
	}
    }

}

/**
 * (e1 e2 ...)
 */
class AppParser extends ParseClass {
    
    public AST parse( DynamicObject s, SyntacticEnvironment m ) 
	throws ParseException {
	if ( s.isPair() ) {
	    Pair p = (Pair)s;
	    DynamicObject car = p.head();
	    DynamicObject cdr = p.tail();
	    DynamicObject argList = cdr;
	    List<AST> args = new LinkedList<AST>();
	    while ( argList.isPair() ) {
		Pair argp = (Pair)argList;
		DynamicObject arg = argp.head();
		DynamicObject argcdr = argp.tail();
		args.add( AST.parse( arg, m ) );
		argList = argcdr;
	    }
	    if ( !argList.isNil() ) {
		throw new ParseException( "Improper list in function application", 0 );
	    }
	    return new App( AST.parse( car, m ), args );
	} else {
	    throw new ParseException( "Improper function application", 0 );
	}	    
    }

}

/**
 * x
 */
class VarParser extends ParseClass {
   
    public AST parse( DynamicObject s, SyntacticEnvironment m ) 
	throws ParseException {
	if ( s.isSymbol() ) {
	    return new Var( (Symbol)s );
	} else {
	    throw new ParseException( "Variable must be a single symbol", 0 );
	}
    }

}

/**
 * n
 */
class ConstantParser extends ParseClass {
   
    public AST parse( DynamicObject s, SyntacticEnvironment m ) 
	throws ParseException
    {
	return new Constant( s );
    }

}

/**
 * (define s e)
 */
class DefineParser extends ParseClass {

    private SyntacticEnvironment nonDefinitionalEnv;

    public DefineParser( SyntacticEnvironment e ) {
	nonDefinitionalEnv = e;
    }
    
    public AST parse( DynamicObject s, SyntacticEnvironment m ) 
	throws ParseException {
	if ( s.isPair() && ((Pair)s).tail().isPair() &&
	     ((Pair)((Pair)s).tail()).tail().isPair() &&
	     ((Pair)((Pair)((Pair)s).tail()).tail()).tail().isNil() ) {
	    Pair p1 = (Pair)s;
	    DynamicObject car = p1.head();
	    DynamicObject cdr = p1.tail();
	    Pair p2 = (Pair)cdr;
	    DynamicObject cadr = p2.head();
	    DynamicObject cddr = p2.tail();
	    Pair p3 = (Pair)cddr;
	    DynamicObject caddr = p3.head();
	    if ( !cadr.isSymbol() )
		throw new ParseException( "Attempt to define non-symbol variable", 0 );
	    return new Define( (Symbol)cadr, 
			       AST.parse( caddr, nonDefinitionalEnv ) );
	} else {
	    throw new ParseException( "Ill-formed define", 0 );
	}
    }
}
	    
/**
 * (defineSyntax keyword parse-function)
 */
class DefineSyntaxParser extends ParseClass {

    private SyntacticEnvironment nonDefinitionalEnv;
    private MutableSyntacticEnvironment mutableEnv;

    public DefineSyntaxParser( SyntacticEnvironment nde,
			       MutableSyntacticEnvironment me ) {
	nonDefinitionalEnv = nde;
	mutableEnv = me;
    }

    public AST parse( DynamicObject s, SyntacticEnvironment m )
	throws ParseException {
	if ( s.isPair() && ((Pair)s).tail().isPair() &&
	     ((Pair)((Pair)s).tail()).tail().isPair() &&
	     ((Pair)((Pair)((Pair)s).tail()).tail()).tail().isNil() ) {
	    Pair p1 = (Pair)s;
	    DynamicObject car = p1.head();
	    DynamicObject cdr = p1.tail();
	    Pair p2 = (Pair)cdr;
	    DynamicObject cadr = p2.head();
	    DynamicObject cddr = p2.tail();
	    Pair p3 = (Pair)cddr;
	    DynamicObject caddr = p3.head();
	    if ( !cadr.isSymbol() )
		throw new ParseException( "Attempt to define non-symbol variable", 0 );
	    AST parserAST = AST.parse( caddr, nonDefinitionalEnv );
	    Closure parserEvaluatorThunk = 
		AST.compileThunk( parserAST, AST.ployClassLoader );
	    DynamicObject parser;
	    try {
		parser = parserEvaluatorThunk.invoke0();
	    } catch ( RuntimeError r ) {
		throw new ParseException( "Runtime error while evaluating syntax: " + r.getMessage(), 0 );
	    }
	    if ( !parser.isClosure() || (((Closure)parser).numFormals != 1)) {
		throw new ParseException( "Parser must be function of 1 argument", 0 );
	    }
	    mutableEnv.addParser( (Symbol)cadr, 
				  new UserParser((Closure)parser) );
	    return new DefineSyntax( (Symbol)cadr, parserAST );
	} else {
	    throw new ParseException( "Ill-formed define", 0 );
	}
    }
}

/**
 * (something somethingElse ...)
 * i.e. a wrapper for a user macro
 */
class UserParser extends ParseClass {

    Closure parser;

    public UserParser( Closure p ) {
	parser = p;
    }

    public AST parse( DynamicObject s, SyntacticEnvironment m )
	throws ParseException {
	
	DynamicObject transformedS;

	try {
	    transformedS = parser.invoke1( s );
	} catch ( RuntimeError r ) {
	    throw new ParseException( "Runtime error during macro expansion: " +
				      r.getMessage( ), 0 );
	}
	
	return AST.parse( transformedS, m );
    }
}